#!/usr/bin/env python
# Copyright 2010-2013 RethinkDB, all rights reserved.

"""This script is used to generate the RDB_MAKE_SEMILATTICE_JOINABLE_*() macro
definitions.

This script is meant to be run as follows (assuming you are in the
"rethinkdb/src" directory):

$ ../scripts/generate_join_macros.py > rpc/semilattice/joins/macros.hpp

"""



import sys


def help_generate_semilattice_joinable_macro(nfields, impl):
    print("#define RDB_%s_SEMILATTICE_JOINABLE_%d(type_t%s) \\" % \
        (("IMPL" if impl else "MAKE"), nfields, "".join(", field%d" % (i + 1) for i in range(nfields))))
    unused = "UNUSED " if nfields == 0 else ""
    print("    %svoid semilattice_join(%stype_t *_a_, %sconst type_t &_b_) { \\" % ("" if impl else "inline ", unused, unused))
    for i in range(nfields):
        print("        semilattice_join(&_a_->field%d, _b_.field%d); \\" % (i + 1, i + 1))
    print("    } \\")
    # Putting this here makes us require a semicolon after macro invocation.
    print("    extern int semilattice_joinable_force_semicolon_declaration")

def generate_make_semilattice_joinable_macro(nfields):
    help_generate_semilattice_joinable_macro(nfields, False)

def generate_impl_semilattice_joinable_macro(nfields):
    help_generate_semilattice_joinable_macro(nfields, True)

def help_generate_equality_comparable_macro(nfields, impl):
    print("#define RDB_%s_EQUALITY_COMPARABLE_%d(type_t%s) \\" % \
        (("IMPL" if impl else "MAKE"), nfields, "".join(", field%d" % (i + 1) for i in range(nfields))))
    unused = "UNUSED " if nfields == 0 else ""
    print("    %sbool operator==(%sconst type_t &_a_, %sconst type_t &_b_) { \\" % ("" if impl else "inline ", unused, unused))
    if nfields == 0:
        print("        return true; \\")
    else:
        print("        return " + " && ".join("_a_.field%d == _b_.field%d" % (i + 1, i + 1) for i in range(nfields)) + "; \\")
    print("    } \\")
    # Putting this here makes us require a semicolon after macro invocation.
    print("    extern int equality_force_semicolon_declaration")

def generate_make_equality_comparable_macro(nfields):
    help_generate_equality_comparable_macro(nfields, False)

def generate_impl_equality_comparable_macro(nfields):
    help_generate_equality_comparable_macro(nfields, True)

def generate_make_me_equality_comparable_macro(nfields):
    print("#define RDB_MAKE_ME_EQUALITY_COMPARABLE_%d(type_t%s) \\" % \
        (nfields, "".join(", field%d" % (i + 1) for i in range(nfields))))
    unused = "UNUSED " if nfields == 0 else ""
    print("    bool operator==(%sconst type_t &_a_) const { \\" % unused)
    if nfields == 0:
        print("        return true; \\")
    else:
        print("        return " + " && ".join("field%d == _a_.field%d" % (i + 1, i + 1) for i in range(nfields)) + "; \\")
    print("    } \\")
    print("    friend class equality_force_semicolon_declaration_t")

if __name__ == "__main__":

    print("// Copyright 2010-2013 RethinkDB, all rights reserved.")
    print("#ifndef RPC_SEMILATTICE_JOINS_MACROS_HPP_")
    print("#define RPC_SEMILATTICE_JOINS_MACROS_HPP_")
    print()

    print("/* This file is automatically generated by '%s'." % " ".join(sys.argv))
    print("Please modify '%s' instead of modifying this file.*/" % sys.argv[0])
    print()

    print("""
/* The purpose of these macros is to make it easier to define semilattice joins
for types that consist of a fixed set of fields which it is a simple product of.
In the same namespace as the type, call `RDB_MAKE_SEMILATTICE_JOINABLE_[n]()`,
where `[n]` is the number of fields in the type. The first parameter is the name
of the type; the remaining parameters are the fields. You will also need an
`==` operator; for this you can use `RDB_MAKE_EQUALITY_COMPARABLE_[n]()`.

Example:
    struct point_t {
        vclock_t<int> x, y;
    };
    RDB_MAKE_SEMILATTICE_JOINABLE_2(point_t, x, y)

You can also use this with templated types, but it's pretty hacky:
    template<class T>
    struct pair_t {
        T a, b;
    };
    template<class T>
    RDB_MAKE_SEMILATTICE_JOINABLE_2(pair_t<T>, a, b)
*/

#define RDB_DECLARE_SEMILATTICE_JOINABLE(type_t) \\
  void semilattice_join(type_t *, const type_t &)

#define RDB_DECLARE_EQUALITY_COMPARABLE(type_t) \\
  bool operator==(const type_t &, const type_t &)
    """.strip())

    for nfields in range(0, 20):
        generate_make_semilattice_joinable_macro(nfields)
        generate_impl_semilattice_joinable_macro(nfields)
        generate_make_equality_comparable_macro(nfields)
        generate_impl_equality_comparable_macro(nfields)
        generate_make_me_equality_comparable_macro(nfields)
        print()

    print("#endif  // RPC_SEMILATTICE_JOINS_MACROS_HPP_")
